#pragma once
#include "InetAddr.hpp"
#include "Common.hpp"
#include <sys/types.h>
#include "Socket.hpp"
#include <sys/wait.h>
#include <memory>
#include <sys/time.h>
#include <unistd.h>
// #include "Cal.hpp"
// #include "Protocol.hpp"

namespace kzz
{
    class SelectServer
    {
        public:
        const static int size = sizeof(fd_set) * 8;
        const static int defaultfd = -1;

        SelectServer(int port)
            : _listensockfd(std::make_unique<TcpSocket>()), _port(port)
        {
            _listensockfd->BuildTcpServerSocketMethod(_port);
            for (int i = 0; i < size; i++)
            {
                _fd_array[i] = defaultfd;
            }
            _fd_array[0] = _listensockfd->Fd();
        }
        void Start()
        {

            _isrunning = true;

            while (_isrunning)
            {

                int maxfd = 0;
                fd_set rfds;
                FD_ZERO(&rfds);
                // //将listen套接字设置进rfds
                // FD_SET(_listensockfd->Fd(),&rfds);
                // 将辅助数组中所有文件描述符信息设置进rfds

                for (int i = 0; i < size; i++)
                {
                    if (_fd_array[i] == defaultfd)
                        continue;

                    FD_SET(_fd_array[i], &rfds);
                    // 更新最大文件描述符
                    if (_fd_array[i] > maxfd)
                        maxfd = _fd_array[i];
                }

                int n = select(maxfd + 1, &rfds, nullptr, nullptr, nullptr);
                switch (n)
                {
                case -1: // 出错
                    LOG(LogLevel::ERROR) << "select errror";
                    break;
                case 0: // 超时
                    LOG(LogLevel::ERROR) << "timeout";
                    break;
                default: // 有可以处理的事务了
                    LOG(LogLevel::DBUGER) << "有事务就绪了......";
                    Dispatcher(rfds); // 处理就绪事务
                    break;
                }
            }
        }
        void Accepter()
        {
            InetAddr clientaddr;
            int fd = _listensockfd->Accept(clientaddr)->Fd();
            // 将新获取到的文件描述符放入辅助数组中
            if (fd > 0)
            {
                int pos = 0;
                for (; pos < size; pos++)
                {
                    if (_fd_array[pos] == defaultfd)
                    {
                        break;
                    }
                }
                if(pos==size)
                {
                    LOG(LogLevel::DBUGER)<<"select full";
                    return ;
                }
                _fd_array[pos]=fd;
            }
        }
        //传入fd和pos,如果对端关闭，把pos从辅助数组中拿出来
        void Recv(int fd,int pos)
        {
            char buffer[1024];
            int n=recv(fd,buffer,sizeof(buffer),0);
            if(n<0)
            {
                //出错
                
                LOG(LogLevel::DBUGER)<<fd<<"出错";
                close(fd);
                _fd_array[pos]=defaultfd;

            }
            else if(n==0)
            {
                //对端关闭
                LOG(LogLevel::DBUGER)<<fd<<"对端关闭";
                close(fd);
                _fd_array[pos]=defaultfd;
            }
            else
            {
                buffer[n]=0;
                std::cout<<"client echo:"<<buffer<<std::endl;
            }


        }
        void Dispatcher(fd_set rfds)
        {
            // 检查辅助数组中的谁就绪了
            for (int i = 0; i < size; i++)
            {
                if (_fd_array[i] == defaultfd)
                    continue;

                // 判断这个文件是否在rfds中
                if (FD_ISSET(_fd_array[i], &rfds))
                {
                    // 再判断是listen还是其他
                    if (_fd_array[i] == _listensockfd->Fd())
                    {
                        // listen来新连接了
                        Accepter();
                    }
                    else
                    {
                        // 普通事件
                        Recv(_fd_array[i],i);
                    }
                }
            }
        }

    private:
        std::unique_ptr<Socket> _listensockfd;
        uint16_t _port;
        bool _isrunning;
        int _fd_array[size];
    };

}